package com.ecar.api.oauth;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.security.oauth2.http.converter.jaxb.JaxbOAuth2ExceptionMessageConverter;
import org.springframework.security.oauth2.provider.error.DefaultOAuth2ExceptionRenderer;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * Created by admin on 2016/12/23.
 */
public class ExceptionRenderer extends DefaultOAuth2ExceptionRenderer {

	private static final Logger log = LoggerFactory.getLogger(ExceptionRenderer.class);
	private List<HttpMessageConverter<?>> messageConverters = geDefaultMessageConverters();

	public ExceptionRenderer() {
	}

	public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
		this.messageConverters = messageConverters;
	}

	public void handleHttpEntityResponse(HttpEntity<?> responseEntity, ServletWebRequest webRequest) throws Exception {
		if (responseEntity != null) {
			HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
			HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);
			if (responseEntity instanceof ResponseEntity && outputMessage instanceof ServerHttpResponse) {
				((ServerHttpResponse) outputMessage).setStatusCode(((ResponseEntity) responseEntity).getStatusCode());
//				((ServerHttpResponse) outputMessage).setStatusCode(HttpStatus.valueOf(200));
			}

			HttpHeaders entityHeaders = responseEntity.getHeaders();
			if (!entityHeaders.isEmpty()) outputMessage.getHeaders().putAll(entityHeaders);

			Object body = responseEntity.getBody();
			if (body != null) {
				writeWithMessageConverters(body, inputMessage, outputMessage);
			} else {
				outputMessage.getBody();
			}
		}
	}

	/** @noinspection WhileLoopReplaceableByForEach */
	private void writeWithMessageConverters(Object returnValue,
											HttpInputMessage inputMessage,
											HttpOutputMessage outputMessage)
		throws IOException, HttpMediaTypeNotAcceptableException {

		List acceptedMediaTypes = inputMessage.getHeaders().getAccept();
		if (acceptedMediaTypes.isEmpty()) {
			acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
		}

		MediaType.sortByQualityValue(acceptedMediaTypes);
		Class returnValueType = returnValue.getClass();
		ArrayList allSupportedMediaTypes = new ArrayList();
		Iterator mediaTypes = acceptedMediaTypes.iterator();
		while (mediaTypes.hasNext()) {
			MediaType messageConverter = (MediaType) mediaTypes.next();
			Iterator converters = messageConverters.iterator();

			while (converters.hasNext()) {
				HttpMessageConverter messageConverter1 = (HttpMessageConverter) converters.next();
				if (messageConverter1.canWrite(returnValueType, messageConverter)) {
					messageConverter1.write(returnValue, messageConverter, outputMessage);
					if (log.isDebugEnabled()) {
						MediaType contentType = outputMessage.getHeaders().getContentType();
						if (contentType == null) contentType = messageConverter;

						log.debug("Written [" + returnValue + "] as \"" + contentType + "\" using [" + messageConverter1 + "]");
					}

					return;
				}
			}
		}

		mediaTypes = messageConverters.iterator();
		while (mediaTypes.hasNext()) {
			HttpMessageConverter messageConverter2 = (HttpMessageConverter) mediaTypes.next();
			allSupportedMediaTypes.addAll(messageConverter2.getSupportedMediaTypes());
		}

		throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
	}

	private List<HttpMessageConverter<?>> geDefaultMessageConverters() {
		ArrayList result = new ArrayList();
		result.add(new JsonOAuth2MessageConverter());
		result.addAll((new RestTemplate()).getMessageConverters());
		result.add(new JaxbOAuth2ExceptionMessageConverter());
		return result;
	}

	private HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception {
		HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
		return new ServletServerHttpRequest(servletRequest);
	}

	private HttpOutputMessage createHttpOutputMessage(NativeWebRequest webRequest) throws Exception {
		HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse();
		return new ServletServerHttpResponse(servletResponse);
	}
}
